Conversation
OpenAPI ChangesNo detectable change. Unexpected changes? Ensure your branch is up-to-date with |
d604868 to
9731e2c
Compare
|
@zamanafzal could you review this? |
There was a problem hiding this comment.
Pull request overview
This PR centralizes DRF pagination behavior and optimizes pagination count queries by counting against a reduced field set (defaulting to pk) to avoid expensive COUNT(DISTINCT <many columns>) subqueries on complex querysets.
Changes:
- Introduces
main.pagination.DefaultPagination(andLargePagination) and sets it as the global DRFDEFAULT_PAGINATION_CLASS. - Removes per-app pagination classes/overrides in favor of the global default, and explicitly opts out (
pagination_class = None) for endpoints that must preserve non-paginated responses. - Adjusts the learning-resources
summaryaction queryset to avoid incompatibleselect_related()behavior and reduce selected columns.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| video_shorts/views.py | Switches video shorts pagination to subclass the new global default pagination. |
| profiles/views.py | Explicitly disables pagination for the staff user admin endpoint to preserve its response shape. |
| news_events/views.py | Removes the app-local pagination implementation and relies on the global default pagination. |
| main/settings.py | Sets DRF DEFAULT_PAGINATION_CLASS to main.pagination.DefaultPagination. |
| main/pagination.py | Adds centralized DefaultPagination/LargePagination and overrides get_count() to count with fewer selected fields. |
| learning_resources_search/views.py | Disables pagination for the user subscription endpoint to preserve its response shape. |
| learning_resources/views.py | Removes local pagination classes, adopts centralized pagination, and updates the summary action queryset. |
| channels/views.py | Relies on global pagination for channel lists and disables pagination on non-list-like endpoints. |
| articles/views.py | Removes the app-local pagination implementation and relies on the global default pagination. |
| """ | ||
| queryset = self.filter_queryset( | ||
| self.get_queryset().values("id", "last_modified") | ||
| # we don't use `self.get_queryset()` here because there are incomplatible |
There was a problem hiding this comment.
Typo in comment: "incomplatible" should be "incompatible".
| # we don't use `self.get_queryset()` here because there are incomplatible | |
| # we don't use `self.get_queryset()` here because there are incompatible |
| ) | ||
| from main.constants import VALID_HTTP_METHODS | ||
| from main.filters import MultipleOptionsFilterBackend | ||
| from main.pagination import LargePagination |
There was a problem hiding this comment.
Removing DefaultPagination/LargePagination from learning_resources.views breaks existing imports (e.g., testimonials/views.py imports LargePagination from learning_resources.views). Update those imports/usages to point to main.pagination (or re-export from learning_resources.views) to avoid ImportError at runtime.
| from main.pagination import LargePagination | |
| from main.pagination import DefaultPagination, LargePagination |
There was a problem hiding this comment.
I think we need to make this change at
mit-learn/testimonials/views.py
Line 7 in 088cac9
| # we don't use `self.get_queryset()` here because there are incomplatible | ||
| # `select_related()` invocations and we don't need related data anyway | ||
| LearningResource.objects.filter(published=True).only("id", "last_modified") | ||
| ) |
There was a problem hiding this comment.
The summary action now builds its own base queryset without .distinct(). Because LearningResource filters include many-to-many relations (e.g., topics/departments), filtering can introduce duplicate rows unless the queryset is made distinct. Consider applying .distinct() after filter_queryset (or otherwise ensuring uniqueness) to preserve correct counts/results.
| ) | |
| ).distinct() |
zamanafzal
left a comment
There was a problem hiding this comment.
@rhysyngsun Thanks for the PR. The pagination count optimisation looks useful. I've left a few comments on the PR. Do you think they make sense?
| ) | ||
| from main.constants import VALID_HTTP_METHODS | ||
| from main.filters import MultipleOptionsFilterBackend | ||
| from main.pagination import LargePagination |
There was a problem hiding this comment.
I think we need to make this change at
mit-learn/testimonials/views.py
Line 7 in 088cac9
learning_resources/views.py
Outdated
| self.get_queryset().values("id", "last_modified") | ||
| # we don't use `self.get_queryset()` here because there are incomplatible | ||
| # `select_related()` invocations and we don't need related data anyway | ||
| LearningResource.objects.filter(published=True).only("id", "last_modified") |
There was a problem hiding this comment.
The changes to LearningResourceViewSet.summary() appear to bypass self.get_queryset() and the original deduplication logic. Since summary is used for sitemaps, could we add a test (or at least do a manual check) to ensure the count and the returned results stay consistent with the main list endpoint for the same filters (i.e., no duplicate-driven count inflation or paging gaps)?
| "DEFAULT_AUTHENTICATION_CLASSES": ( | ||
| "rest_framework.authentication.SessionAuthentication", | ||
| ), | ||
| "DEFAULT_PAGINATION_CLASS": "main.pagination.DefaultPagination", |
There was a problem hiding this comment.
Since we're now setting DEFAULT_PAGINATION_CLASS in settings, some endpoints might switch from returning a plain list ([]) to a full paginated response ({count, next, previous, results}).
Even with the pagination_class = None you added in a few places, do you think we should do a quick audit of all list endpoints to make sure we haven't accidentally changed the response shape for any frontend consumers?
There was a problem hiding this comment.
(doesn't hurt to check, but this is the sort of thing our OpenAPI CI check should actualy be very good at catching)
mbertrand
left a comment
There was a problem hiding this comment.
Looks good, agree with other comments about:
- adding
distinct()to the LearnignResourceViewSet summary view and tests to make sure counts are as expected with no dupes - updating the LargePagination import for testimonials
62fb2f7 to
03f664d
Compare
zamanafzal
left a comment
There was a problem hiding this comment.
@rhysyngsun The changes look good to me. The only thing missing is tests to verify the LearningResourceViewSet summary view to make sure counts are as expected. If you think tests aren’t necessary, then we’re good to proceed with these changes.
What are the relevant tickets?
Description (What does it do?)
pkby default (this can be overridden if needed by subclassing).The effect of this to turn queries like this that DRF previously ran to determine
count(this example is for the/api/v1/featured/endpoint):into a query like this:
The difference in performance between these two specific queries is ~1000x faster, going from ~500ms for the version currently in
mainto0.3msfor this branch. I don't expect to see exactly the same improvement across the board, but there should be some kind of improvement because the count query won't be going to disk as much for data and ideally just hits a few indices now.How can this be tested?
The app should still function. You likely won't see a huge difference in performance locally unless you have production-scale data around learning resources.